package org.inet.minispring.bean.beanfactory;

import org.inet.minispring.BeanDefinition;
import org.inet.minispring.bean.beandefinition.registry.BeanDefinitionRegistry;
import org.inet.minispring.bean.beanregistry.DefaultSingletonBeanRegistry;
import org.inet.minispring.exception.BeansException;

import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class SimpleBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory, BeanDefinitionRegistry {

    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    private final List<String> beanDefinitionNames = new ArrayList<>();

    public SimpleBeanFactory() {}

    @Override
    public Object getBean(String beanName) throws BeansException {
        // 先尝试直接获取 bean
        Object singleton = singletons.get(beanName);
        // 如果 bean 还未创建，则获取其定义来创建实例
        if (Objects.isNull(singleton)) {
            var beanDefinition = beanDefinitionMap.get(beanName);
            if (Objects.isNull(beanDefinition)) {
                throw new BeansException("BeanDefinition %s cannot found".formatted(beanName));
            }
            try {
                singleton = Class.forName(beanDefinition.getClassName()).getDeclaredConstructor().newInstance();
            } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException |
                     InstantiationException | IllegalAccessException e) {
                throw new BeansException("Cannot new instance: " + beanDefinition.getClassName());
            }
            super.registerSingleton(beanName, singleton);
        }
        return singleton;
    }

    @Override
    public boolean containsBean(String beanName) {
        return super.containsSingleton(beanName);
    }

    @Override
    public boolean isSingleton(String beanName) {
        return Objects.equals(beanDefinitionMap.get(beanName).getScope(), BeanDefinition.SCOPE_SINGLETON);
    }

    @Override
    public boolean isPrototype(String beanName) {
        return Objects.equals(beanDefinitionMap.get(beanName).getScope(), BeanDefinition.SCOPE_PROTOTYPE);
    }

    @Override
    public Class<?> getType(String beanName) {
        return beanDefinitionMap.get(beanName).getClass();
    }

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
        this.beanDefinitionMap.put(beanName, beanDefinition);
        this.beanDefinitionNames.add(beanName);
        if (!beanDefinition.isLazyInit()) {
            try {
                getBean(beanName);
            } catch (BeansException ignore) {
            }
        }
    }

    @Override
    public void removeBeanDefinition(String name) {
        beanDefinitionMap.remove(name);
        beanDefinitionNames.remove(name);
        removeSingleton(name);
    }

    @Override
    public BeanDefinition getBeanDefinition(String name) {
        return beanDefinitionMap.get(name);
    }

    @Override
    public boolean containsBeanDefinition(String name) {
        return beanDefinitionMap.containsKey(name);
    }


}
